/**
 * ExtDirect4Java
 * @author Steve Jacot-Guillarmod
 * @version 0.1-alpha
 *
 * Copyright(c) 2009, Swissdotnet S�rl
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 */
package ch.swissdotnet.extdirect4j;


import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;
import java.util.Map;


class Utils {

    public static Class getClassByName(final String className) {
        try {
            return Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new ExtDirectException("class " + className + " not found", e);
        }
    }

    static JsonDeserializer<ExtDirectQuery> getQueryDeserializer(final Map<String, AbstractAction> actionMap) {
        return new JsonDeserializer<ExtDirectQuery>() {
            @SuppressWarnings("unchecked")
            public ExtDirectQuery deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException {
                final ExtDirectQuery query = new ExtDirectQuery();
                final JsonObject jsonObj = json.getAsJsonObject();
                query.setAction(jsonObj.get("action").getAsString());
                query.setMethod(jsonObj.get("method").getAsString());
                query.setType(ExtDirectDTO.Type.valueOf(jsonObj.get("type").getAsString()));
                query.setTid(jsonObj.get("tid").getAsInt());
                final JsonElement data = jsonObj.get("data");
                final JsonArray dataArray = data.isJsonNull() ? new JsonArray() : data.getAsJsonArray();
                final int dataSize = dataArray.size();
                final String actionName = query.getAction();
                final String methodName = query.getMethod();
                Class[] paramsType = this.getParametersType(actionName, methodName, dataSize);
                if (paramsType.length != dataSize) {
                    throw new ExtDirectException("incorrect number of parameters for a method: "
                            + actionName + "#" + methodName);
                }
                for (int i = 0; i < dataSize; i++) {
                    query.getData().add(context.deserialize(dataArray.get(i), paramsType[i]));
                }
                return query;
            }

            private Class[] getParametersType(final String actionName, final String methodName, final int argNum) {
                AbstractAction action = actionMap.get(actionName);
                if (action == null) {
                    throw new ExtDirectException("invalid action: " + actionName + " requested");
                }
                Class[] paramsType = action.getParametersType(methodName, argNum);
                if (paramsType == null) {
                    throw new ExtDirectException("invalid method: " + methodName + " requested");
                }
                return paramsType;
            }
        };
    }

    static JsonSerializer<AbstractAction> getActionSerializer() {
        return new JsonSerializer<AbstractAction>() {
            public JsonElement serialize(final AbstractAction action, final Type typeOfSrc, final JsonSerializationContext context) {
                final JsonArray methods = new JsonArray();
                for (Map.Entry<String, Map<Integer, ExtDirectMethod>> e1 : action.getMethodMap().entrySet()) {
                    for (Map.Entry<Integer, ExtDirectMethod> e2 : e1.getValue().entrySet()) {
                        JsonObject method = new JsonObject();
                        method.addProperty("name", e1.getKey());
                        method.addProperty("len", e2.getKey());
                        methods.add(method);
                    }
                }
                return methods;
            }
        };
    }

    static void commonChecking(ExtDirectQuery query) {
        if (!query.getType().equals(ExtDirectDTO.Type.rpc)) {
            throw new ExtDirectException("request type '" + query.getType()
                    + "' is not supported. it should be only 'rpc'");
        }
        if (query.getAction() == null) {
            throw new ExtDirectException("requested action name should not be null");
        }
        if (query.getMethod() == null) {
            throw new ExtDirectException("requested method name should not be null");
        }
    }
}
